// Copyright  2012 Petr TALLA. [petr.talla@gmail.com]
// This file is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY;  without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
// [http://www.gnu.org/copyleft/lesser.html]
//=============================================================================

package tc.crase.view;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;
import java.io.IOException;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import tc.common.registry.Obj;
import tc.common.registry.Ref;
import tc.common.registry.RegistrySys;
import tc.common.registry.Rview;
import tc.common.utility.Dynbuff;
import tc.common.utility.DynbuffRect;
import tc.common.utility.GrView;
import tc.common.utility.TcCmd;

import tc.crase.cmds.CmdEx;
import tc.crase.cmds.Selection;
import tc.crase.db.Xattr;
import tc.crase.db.XattrGRP;
import tc.crase.db.XattrGXY;
import tc.crase.db.XattrLNK;
import tc.crase.db.XattrSTR;
import tc.crase.db.XattrTPE;
import tc.crase.db.XattrURL;
import tc.crase.meta.Attrdef;
import tc.common.symbology.Style;
import tc.crase.meta.Type;
import tc.crase.sys.XdbEx;
import tc.crase.sys.XobjectEx;
import tc.crase.sys.Xpair;

public class ViewXObject extends JPanel implements Rview, MouseListener, MouseMotionListener, ActionListener, ComponentListener {
	
	private Ref obj = null;
	private int attrIndex = -1;
	
	public JPanel panel;
	private JTextField editor;
	public JInternalFrame frame; 
	
	ActiveAreaCol actives = new ActiveAreaCol(); 
	
	public ViewXObject () {
		RegistrySys.registry.views.add(this);
		
		addMouseListener((MouseListener)this); 
		addMouseMotionListener((MouseMotionListener)this);

		setLayout(null);
		
		editor = new JTextField();
		editor.addActionListener(this);
		editor.setActionCommand("enter");
		editor.setVisible(true);
		add(editor);
		
		panel = this;
	}
	
	public void removeNotify() {
		super.removeNotify();
	}
	
	public XobjectEx getObj() {
		if ( obj == null ) return null;
		if ( obj.original instanceof XobjectEx.ObjEx == false) return null;
		XobjectEx.ObjEx result = (XobjectEx.ObjEx)(obj.original); 
		return result.getObject();
	}
	
	public void setObj(XobjectEx obj) {
		if (obj == null) {
			this.obj = null;
		}
		else
		{	this.obj = new Ref(obj.objex, this);
			dirty = true;
		}
		
		RegistrySys.registry.lock();
		RegistrySys.registry.unlock();
	}
	
	public void paint(Graphics g) {
		Area clip = new Area(getBounds());
		clip.exclusiveOr(new Area(editor.getBounds()));
		g.setClip(clip);
		display(g, this);
		if (Dynbuff.view == this ) {
			Dynbuff.dynamicBufferPaint();
		}
		editor.repaint();
	}
	
	public static int LINE_HEIGHT = 20;
	public static int LINE_UNDER  = 4;
	public static int HEADER_HEIGHT  = 60;
	public static int LINE_LEFT = 4;
	
	public static int lineY(int index) {
		return HEADER_HEIGHT + index*20;
	}
	
	public static int left(GrView view) {
		return 0;
	}
	
	public void displayTPE (Graphics g, XattrTPE attr, int X, int Y) {
		g.drawString(attr.getAsTPE().id, X, Y);
		
		if (Selection.attrsAllowed == false) return;
		Rectangle r = new Rectangle(2, Y-LINE_HEIGHT+LINE_UNDER, getWidth()-5, LINE_HEIGHT);
		actives.items.add(new ActiveArea(r, ActiveArea.Type.ATTR, new Xpair(getObj(),attr) ));
	}
	
	public void displayIcon (Graphics g, XobjectEx opposite, int X, int Y) {
		Style style = null;
		
		XdbEx xdbex = opposite.getDb();
		style = (Style)xdbex.metadata.legend.styles.get(opposite.getType());
		
		if (style != null) {
			style.drawIcon( g, X+2, Y-14, 14, 14);
		}
		else
		{	g.drawRect( X+2, Y-14, 14, 14);		
		}
	}
	
	public void displayObject (Graphics g, XobjectEx object, int X, int Y) {
		if (object.getSelected()) {
			ViewRoot.styleSelected.drawIcon( g, X, Y-LINE_HEIGHT+4, getWidth()-X, LINE_HEIGHT);
		}
		
		int Xtxt = X;
		if (object != null)
		{	Xtxt += 22;
			displayIcon (g, object, X, Y);
		}
		
		String name = "BROKEN LINK";
		if (object != null) name = Formatter.shortFormat(object);
		
		Font oldFont = g.getFont();
		g.setFont(new Font(oldFont.getName(), Font.BOLD, oldFont.getSize()) );
		g.drawString(name ,Xtxt, Y);
		g.setFont(oldFont);
		
		if (Selection.objectsAllowed == false) return;
		if (object == null) return;
		Rectangle r = new Rectangle(X, Y-LINE_HEIGHT+LINE_UNDER, getWidth()-X-4, LINE_HEIGHT);
		actives.items.add(new ActiveArea(r, ActiveArea.Type.OBJECT, object));
	}
	
	public void displayGXY (Graphics g, XattrGXY gxy, int X, int Y) {
		XattrGRP attrGRP = gxy.getGroup();
		
		XobjectEx object = null;
		if ( attrGRP != null ) object = (XobjectEx)attrGRP.parent;
		
		displayObject(g, object, X, Y);
	}
	
	public void displayLNK (Graphics g, XattrLNK lnk, int X, int Y) {
		XattrLNK oppositeLNK = lnk.getOpposite();
		
		XobjectEx opposite = null;
		if ( oppositeLNK != null ) opposite = (XobjectEx)oppositeLNK.getParent();
		
		displayObject(g, opposite, X, Y);
	}
	
	public void displaySTR (Graphics g, XattrSTR str, int X, int Y) {
		g.drawString(str.getValue(), X, Y);
		
		Rectangle r = new Rectangle(X, Y-LINE_HEIGHT+LINE_UNDER, getWidth()-X-4, LINE_HEIGHT);
		actives.items.add(new ActiveArea(r, ActiveArea.Type.ATTRVAL, str));
	}
	
	public void displayURL (Graphics g, XattrURL url, int X, int Y) {
		int y = Y-LINE_HEIGHT+LINE_UNDER;
				
		Rectangle r = new Rectangle(X, y, getWidth()-X-4-LINE_HEIGHT-5, LINE_HEIGHT);
		actives.items.add(new ActiveArea(r, ActiveArea.Type.ATTRVAL, url));
		
		Rectangle rect = new Rectangle(getWidth()-5-LINE_HEIGHT, y, LINE_HEIGHT, LINE_HEIGHT);
		ActiveArea aa = new ActiveArea(rect, ActiveArea.Type.ATTRVAL, url);
		aa.run = true;
		actives.items.add(aa);
			
		Color oldColor = g.getColor();
		g.setColor(Color.RED);
		g.fill3DRect(rect.x, rect.y, rect.width, rect.height, true);
		g.setColor(oldColor);
		
		g.drawString(url.getValue(), X, Y);
	}
	
	public void displayGRP (Graphics g, XattrGRP str, int X, int Y) {
		g.drawString(str.getValue(), X, Y);
	}
	
	public void displayAttr (Graphics g, Xattr attr, int Y) {
		int offX = LINE_LEFT;
	
		if ( attr.getAsTPE() == null ) {
			FontMetrics fm = g.getFontMetrics();
			offX += fm.stringWidth(attr.getName() + ": ");
			if (offX < 60) offX = 60;
			
			int x = 2;
			int y = Y-LINE_HEIGHT+LINE_UNDER;
			int w = offX-4; if (w < 4) w = 4; 
			int h = LINE_HEIGHT;
			
			if (XobjectEx.isAttrSeleced(attr)) {
				ViewRoot.styleSelected.drawIcon(g, x, y, w, h);
			}
			
			g.drawString(attr.getName() + ": ", LINE_LEFT, Y);
			
			if (Selection.attrsAllowed ) {
				Rectangle r = new Rectangle(x, y, w, h);
				actives.items.add(new ActiveArea(r, ActiveArea.Type.ATTR, new Xpair(getObj(), attr)));
			}
		}
				
		if ( attr.getAsTPE() != null ) {
			displayTPE(g, attr.getAsTPE(), offX, Y);
		}
		else if ( attr.getAsLNK() != null ) {
			displayLNK (g, attr.getAsLNK(), offX, Y);
		}
		else if ( attr.getAsSTR() != null ) {
			displaySTR (g, attr.getAsSTR(), offX, Y);
		}
		else if ( attr.getAsURL() != null ) {
			displayURL (g, attr.getAsURL(), offX, Y);
		}
		else if ( attr.getAsGXY() != null ) {
			displayGXY (g, attr.getAsGXY(), offX, Y);
		}
		else if ( attr.getAsGRP() != null ) {
			displayGRP (g, attr.getAsGRP(), offX, Y);
		}
	}
	
	public void displayHeader (Graphics g) {
		g.setColor(Color.BLACK);
		
		if ( (obj == null) || (getObj() == null) ) {
			g.drawString( "No object selected", 20, lineY(0) );
			return;
		}

		if (getObj().getSelected()) {
			ViewRoot.styleSelected.drawIcon( g, 0, 0, getWidth(), 40);
		}
		else {
			g.setColor(Color.LIGHT_GRAY);
			g.fillRect(0, 0, getWidth(), 40);
		}
		
		g.setColor(Color.BLACK);
		Font old2Font = g.getFont();
		g.setFont(new Font(old2Font.getName(), Font.BOLD, old2Font.getSize()) );
		String text = getObj().getType() + ": " + Formatter.shortFormat(getObj())+ " [" + getObj().getId() + "]";
		g.drawString( text, 4, 25);
		g.setFont(old2Font);
		
		Rectangle r = new Rectangle(0, 0, getWidth(), 40);
		actives.items.add(new ActiveArea(r, ActiveArea.Type.OBJECT, getObj()));
	}
	
	public void display (Graphics g, JPanel view) {
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, getWidth(), getHeight());
		
		actives.items.clear();
		
		if (obj == null) return; 
		
		displayHeader(g);
		
		int ii = 0;
		
		for ( Xattr attr : getObj() ) {
			displayAttr(g, attr, lineY(ii++));
		}
		
		Type type = null;
		XobjectEx xobj = getObj();
		XdbEx dbex = xobj.getDb();
		if (dbex != null) type = dbex.metadata.types.get(getObj().getType());
		
		int line = ii;
		
		if (type != null) {
			for (int i = 0; i < type.attrdefs.size(); i++) {
				Attrdef def = type.attrdefs.get(i);
				if ( !(def.type == Xattr.Type.STR) && !(def.type == Xattr.Type.URL) ) continue;
				if (def.couldBeAdded(getObj()) == false) continue;
				
				int y0 = lineY(line)+4;
				int y1 = lineY(line+1)+4;
				String text = "Add attr. " + def.name;
				if (def.value.isEmpty() == false) text += "=" + def.value;
				g.drawString(text, 4, y1);
				
				if (def != null) {
					actives.items.add(new ActiveArea(new Rectangle(2, y0, getWidth()-5, y1-y0), ActiveArea.Type.NEW, def));
				}
				
				line++;
			}
		}
				
		int y0 = lineY(line)+4;
		int y1 = lineY(line+1)+4;
		g.drawString("Add generic attribute", 4, y1);
		actives.items.add(new ActiveArea(new Rectangle(2, y0+4, getWidth()-5, y1-y0), ActiveArea.Type.NEW, ActiveArea.genericAttr));
		line++;
		
		y0 = lineY(line)+4;
		y1 = lineY(line+1)+4;
		g.drawString("Add generic type", 4, y1);
		actives.items.add(new ActiveArea(new Rectangle(2, y0+4, getWidth()-5, y1-y0), ActiveArea.Type.NEW, ActiveArea.genericType));

	}
	
	public class ModelNew extends DefaultComboBoxModel {
		public String type = new String("");
		
		void setSelected(String type) {
			for (int i = 0; i < getSize(); i++ )
			{	TypeModelItem item = (TypeModelItem)getElementAt(i);
				if (item == null) continue;
				if (item.type == type) { 
					setSelectedItem(item);
					return;
				}
			}
		}
		
		public ModelNew () {
			addElement(new TypeModelItem("gen. type") );
			addElement(new TypeModelItem("gen. attr") );
		}
		
		public void setSelectedItem(Object item)
		{	TypeModelItem itemEx = (TypeModelItem)item;
			type = itemEx.type;
			super.setSelectedItem(item);
		}
		
		public class TypeModelItem {
			String type;
			
			TypeModelItem(String type) {
				this.type = type;
			}
			
			public String toString() {
				return type;
			}
		}
	}
	
	public void actionPerformed(ActionEvent e) {
	}
		
	public void mousePressed(MouseEvent e) {
		Graphics g = getGraphics();
		Dynbuff.dynamicBufferClean();
		g.dispose();
		
		if (obj == null) return;
				
		if (e.getButton() == MouseEvent.BUTTON1) {
			
			ActiveArea aa = actives.get(e.getPoint());
			
			if (aa != null) {
				if (aa.type == ActiveArea.Type.ATTR) {
					if ( TcCmd.activeCommand instanceof CmdEx == false ) return;
					CmdEx cmd = (CmdEx)TcCmd.activeCommand;
					Xpair p = (Xpair)aa.object;
					cmd.enterSelected(e.getPoint(), null, p);
				}
				else if (aa.type == ActiveArea.Type.NEW) {
					Attrdef attrdef = (Attrdef)aa.object;
					
					if (attrdef == ActiveArea.genericAttr) {
						String result = JOptionPane.showInputDialog(ViewRoot.pane, "Enter attribute name");
						if (result != null) {
							RegistrySys.registry.lock(); {
								getObj().attrAdd(new XattrSTR(result, ""));
							} RegistrySys.registry.unlock();
						}
					}
					else if (attrdef == ActiveArea.genericType) {
						String result = JOptionPane.showInputDialog(ViewRoot.pane, "Enter attribute type");
						if (result != null) {
							RegistrySys.registry.lock(); {
								getObj().attrAdd(new XattrTPE(result));
							} RegistrySys.registry.unlock();
						}						
					}
					else {
						String result = attrdef.value;
								
						if (result.isEmpty()) {
							result = JOptionPane.showInputDialog(ViewRoot.pane, "Enter value of attribute: " + attrdef.name);
						}
						if (result != null) {
							RegistrySys.registry.lock(); {
								Xattr xattr;
								
								if (attrdef.type == Xattr.Type.URL) {
									xattr = new XattrURL(attrdef.name, result);
								}
								else {
									xattr = new XattrSTR(attrdef.name, result);
								}
								
								getObj().attrAdd(xattr);
							} RegistrySys.registry.unlock();
						}
					}
				}
				else if (aa.type == ActiveArea.Type.OBJECT) {
					if ( TcCmd.activeCommand instanceof CmdEx == false ) return;
					CmdEx cmd = (CmdEx)TcCmd.activeCommand;
					cmd.enterSelected(e.getPoint(), null, aa.object);
				}
				else if (aa.type == ActiveArea.Type.ATTRVAL) {
					if (aa.object instanceof XattrSTR) {
						XattrSTR str = (XattrSTR)aa.object;
						String result = JOptionPane.showInputDialog(ViewRoot.pane, "Value of attribute: " + str.getName(), str.getValue());
						if (result == null) return;
						RegistrySys.registry.lock(); {
							str.setValue(result);
							getObj().objex.objChanged();
						} RegistrySys.registry.unlock();
					}
					else if ( (aa.object instanceof XattrURL) && (aa.run == false) ) {
						XattrURL url = (XattrURL)aa.object;
						String result = JOptionPane.showInputDialog(ViewRoot.pane, "Value of attribute: " + url.getName(), url.getValue());
						if (result == null) return;
						RegistrySys.registry.lock(); {
							url.setValue(result);
							getObj().objex.objChanged();
						} RegistrySys.registry.unlock();
					}
					else if ( (aa.object instanceof XattrURL) && (aa.run == true) ) {
						XattrURL url = (XattrURL)aa.object;
						
						try {
							Process p = Runtime.getRuntime().exec("chromium-browser " + url.getValue());
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
					}
				}
			}
		}
		else if (e.getButton() == MouseEvent.BUTTON3) {
			if (e.getPoint().y < 40) {
				TcCmd.activeCommand.enterReset(e.getPoint(), null);
				return;
			}
			
			if ( attrIndex < 0 ) return;
			if ( attrIndex >= getObj().attrCount() ) return;
			
			RegistrySys.registry.lock();
			
			getObj().attrRemove(getObj().attrGet(attrIndex));
			
			attrIndex = -1;
			
			RegistrySys.registry.unlock();
		}
		
		repaint();
	} 
	public void mouseReleased(MouseEvent e) {}    
	public void mouseDragged(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}  
	
		
	boolean dirty = false;
	public boolean getDirty() {return dirty; };
	public void setDirty(boolean value) { dirty = value; };
	public void checkPresence() {};
	public void updateRview() {repaint(); };
	public void onRefChanged(Ref ref) {
		if ( obj == null ) return;
		if ( getObj() == null ) return;
		if ( getObj().objex.state == Obj.State.DELETED ) obj = null;
	};
	
	Area clipa() {
		Area result = new Area(getBounds());
		result.exclusiveOr(ViewRoot.clip(frame));
		return result;
	}
	public void mouseMoved(MouseEvent e) {
		Graphics g = getGraphics();
		g.setClip(clipa());
		Dynbuff.dynamicBufferClean();
		Point pt = e.getPoint();
		ActiveArea aa = actives.get(pt);
		if (aa == null) return;
		DynbuffRect area = new DynbuffRect(aa.rect);
		Dynbuff.dynamicBufferAdd(area, Dynbuff.Buffer.SEL, this);
		g.dispose();
	}
	public void mouseExited(MouseEvent e) {
		Graphics g = getGraphics();
		g.setClip(clipa());
		Dynbuff.dynamicBufferClean();
	}

	@Override public void componentHidden(ComponentEvent arg0) {}
	@Override public void componentMoved(ComponentEvent arg0) {}
	@Override public void componentShown(ComponentEvent arg0) {}
	
	@Override public void componentResized(ComponentEvent arg0) {
		editor.setLocation(50, 100);
		editor.setSize(100, 30);
	}
}


